package com.retry.task.core.config;

import java.util.Map;

import com.google.common.collect.Maps;
import com.retry.task.core.exception.RetryTaskRuntimeException;
import com.retry.task.core.listener.ApplicationStarted;
import com.retry.task.core.config.RetryTaskAutoConfigure.RetryTaskConsumerRegister;
import com.retry.task.core.constants.RetryTaskConstants;
import com.retry.task.core.heart.HeartBeatService;
import com.retry.task.core.task.RetryTaskProperties;
import com.retry.task.core.task.annotation.RetryTaskAnnotation;
import com.retry.task.core.task.interfaces.IRetryTaskListener;
import com.retry.task.core.server.RetryTaskServer;
import com.retry.task.core.utils.BinderUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotationMetadata;

/**
 * @author gao.gwq
 * @version 1.0
 * @date 2021/05/04  1:06 下午
 * @Description TODO
 */
@Configuration
@EnableConfigurationProperties({RetryTaskProperties.class})
@ConditionalOnProperty(name = RetryTaskConstants.ENABLED, matchIfMissing = true)
@Import(RetryTaskConsumerRegister.class)
public class RetryTaskAutoConfigure {

    @Configuration
    @EnableConfigurationProperties({RetryTaskProperties.class})
    @ConditionalOnProperty(name = RetryTaskConstants.ENABLED, matchIfMissing = true)
    public static class RetryTaskConsumerRegister implements ImportBeanDefinitionRegistrar,
        EnvironmentAware, ApplicationContextAware, ApplicationStarted {
        private static final Logger logger = LoggerFactory.getLogger(
            RetryTaskConsumerRegister.class);

        private ConfigurableEnvironment environment;

        private static BeanDefinitionRegistry registry;

        private boolean initFlag = false;

        private static ApplicationContext context;

        private static RetryTaskServer retryTaskServer;

        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            context = applicationContext;
        }

        @Override
        public void doApplicationStarted() {
            if (initFlag == true) {
                return;
            }
            RetryTaskProperties properties = BinderUtils.bind(environment, RetryTaskConstants.PREFIX,
                RetryTaskProperties.class);
            if (properties.isEnable() == false) {
                return;
            }
            try {
                initHeartBeat(properties);
                initClientServer(properties);
                initRetryTaskListener();

            } catch (Exception ex) {
                logger.error("heart tick init fail:{}", ex.getMessage(), ex);
                ((ConfigurableApplicationContext)context).close();
            }
            initFlag = true;
        }

        private synchronized void initClientServer(RetryTaskProperties taskProperties) {
            ClientServerService.getInstance().initClientServer(Integer.valueOf(taskProperties.getClientPort()));
        }

        private synchronized void initRetryTaskListener() {
            String[] allBeanNames = context.getBeanDefinitionNames();
            Map<String, IRetryTaskListener> listenerMap = Maps.newHashMap();
            int retryTaskBeanNameCounter = 0;
            for (String beanName : allBeanNames) {
                Object bean = context.getBean(beanName);
                if (!(bean instanceof IRetryTaskListener)) {
                    continue;
                }
                RetryTaskAnnotation annotation = AnnotationUtils.findAnnotation(bean.getClass(),
                    RetryTaskAnnotation.class);
                if (annotation == null) {
                    continue;
                }
                String key = annotation.projectName() + "#" + annotation.taskName();
                IRetryTaskListener iRetryTaskListener = listenerMap.get(key);
                if (iRetryTaskListener != null) {
                    String errMsg = "retry task projectName " + annotation.projectName() + ",taskName "
                        + annotation.taskName() + " is duplicate";
                    logger.error(errMsg);
                    throw new RetryTaskRuntimeException(errMsg);
                }
                RetryTaskConsumeHolder.addRetryTask(key, (IRetryTaskListener)bean);
                retryTaskBeanNameCounter++;
            }
            logger.info("retry task  bean init sucess ,num is {}", retryTaskBeanNameCounter);
        }

        private synchronized void initHeartBeat(RetryTaskProperties retryTaskProperties) {
            HeartBeatService.initClientHeart(retryTaskProperties);
        }

        @Override
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
            BeanDefinitionRegistry registrys) {
            registry = registrys;
        }

        @Override
        public void setEnvironment(Environment environment) {
            this.environment = (ConfigurableEnvironment)environment;
        }

    }
}
